/**
 * @author Daniel
 */

;(function($) {
	
	"use strict";
	
	var typeahead = {
		
		options: {
			datasource: [] // 静态数据源
			, labelField: '' // 指定哪个field的值作为标签数据
			, valueField: '' // 指定哪个field的值作为值数据
			, ajaxLoad: false // 指定数据源是否每次都是从异步加载得到，即不是静态数据源
			, ajaxUrl: '' // 当ajaxLoad为true时需要指定加载数据源的URL
			, ajaxDataField: '' // 指定返回的JSON数据,哪个field的值作为数据源
			, ajaxParamField: '' // 指定ajax的参数字段名，即关键字的字段名
			, direction: 'down' // up or down
		}
		
		, _Events: {
		}

       	, _dropdownMenuHtml: '<ul class="typeahead dropdown-menu"></ul>'
       	
       	, _dropdownMenuItemHtml: '<li data-value="{1}" data-label="{2}"><a href="#">{3}</a></li>'

		, _dropdownMenu: null
		
		/* status */
		, _focused: false // indicate whether text input element is focused or not
		, _shown: false // indicate whether dropdownmenu is shown or not
		, _mousedover: false //indicate whether dropdownmenu is mouseover or not
		
		, _create: function() {
			var self = this
				, element = self.element;
				
			element.after($(self._dropdownMenuHtml));		
				
		 	self._initDOMObjects();
		 	self._bindEvents();
		 		
		 	if (!self.options.ajaxLoad) {
		 		self._render();
		 	}
		}

		, _initDOMObjects: function() {
		 	var self = this
			  	, element = self.element;
			  	
		 	self._dropdownMenu = element.next();
		}
		
		, _bindEvents: function() {
			var self = this
				, element = self.element
				, dropdownMenu = self._dropdownMenu
				;
			
			element.bind({
				'focus': $.proxy(self._focus, self)
				, 'blur': $.proxy(self._blur, self)
				, 'keydown': $.proxy(self._keydown, self)
				, 'keyup': $.proxy(self._keyup, self)
				, 'click': function() {
					self._hide();
				}
			});
			
			dropdownMenu.on('click', $.proxy(self._click, self))
			.on('mouseenter', 'li', $.proxy(self._mouseenter, self))
			.on('mouseleave', 'li', $.proxy(self._mouseleave, self));		
		}

		, _render: function () {
			var self = this
				, dropdownMenu = self._dropdownMenu
				;
			
			dropdownMenu.html('');	
			self.options.datasource.forEach(function(dataItem) {
				dropdownMenu.append($.formatMessage(self._dropdownMenuItemHtml, self._getValueFromDatasourceitem(dataItem), self._getLabelFromDatasourceitem(dataItem), self._highlighter(self._getLabelFromDatasourceitem(dataItem))));
			});
		}

	  	, _focus: function() {
      		this._focused = true;
	    }
	    
		, _blur: function() {
			var self = this;
			
			self._focused = false;
			if(!self._mousedover && self._shown) self._hide();
		}
		
		, _keydown: function(e) {
			var self = this;
			
			if (e.keyCode == 38 || e.keyCode == 40) { // up arrow or down arrow
				self._move(e);
			}
		}
		
		, _keyup: function(e) {
			var self = this;
			
			switch(e.keyCode) {
				case 40: // down arrow
				case 38: // up arrow
				case 16: // shift
				case 17: // ctrl
				case 18: // alt
					break;
		
				case 9: // tab
				case 13: // enter
					if(!self._shown) return; 
					self._select();
					break;
		
				case 27:
					// escape
					if(!this.shown) return; 
					self._hide();
					break;
		
				default:
					self._lookup();
			}
			
	      	e.stopPropagation()
      		e.preventDefault()
		}
		
		, _show: function() {
			var self = this
				, element = self.element
				, dropdownMenu = self._dropdownMenu
				, pos = $.extend({}, element.position(), {
					height : element.outerHeight()
				})
				;
				
			if (self.options.direction == 'up') {
				dropdownMenu.css({
					top : - ((pos.top || 0) + dropdownMenu.outerHeight()),
					left : pos.left
				}).show();				
			} else if (self.options.direction == 'down') {
				dropdownMenu.css({
					top : pos.top + pos.height,
					left : pos.left
				}).show();
			}

			self._shown = true;
		}
		
		, _hide: function() {
			var self = this;
			
			self._dropdownMenu.hide();
			self._shown = false;
		}
		
		, _select: function() {
			var self = this
				, element = self.element
				, dropdownMenu = self._dropdownMenu
				, activeItem = dropdownMenu.find('.active')
				;
				
			if (activeItem.length) element.val(activeItem.attr('data-label'));
			dropdownMenu.hide();
		}

		, _lookup: function () {
			var self = this
				, element = self.element
				, dropdownMenu = self._dropdownMenu
				, query = element.val()
				;
				
			if (self.options.ajaxLoad) {
			    if (!query) {
			    	self.options.datasource = [];	
			    	self._render();
			    	self._hide();
			    	return;
			    };
			    
			    var paramData = {};
			    paramData[self.options.ajaxParamField] = element.val();
			    $.ajax({
			        url: self.options.ajaxUrl
			        , dataType: 'json'
	                , type: 'POST'
			        , data: paramData
			        , success: function (data) {
			        	self.options.datasource = self._getDSFromOuterDS(data);
			        	if (self.options.datasource) {
			        		self._render();
			        		if (self.options.datasource.length > 0) self._show();
			        		else self._hide();
			        	} else {
			        		alert('load data error');
			        	}
			        }
		        });					
			} else {
				var hasMatchItem = false;
				dropdownMenu.find('li').removeClass('active').hide().each(function(idx, liItem) {
					if (self._matcher($(liItem).data('label'))) {
						$(liItem).show().find('a').html(self._highlighter($(liItem).data('label')));
						hasMatchItem = true;
					}
				});
				
				if (hasMatchItem) self._show();
				else self._hide();
			}
		}
		
		, _matcher: function (item) {
			var self = this
				, element = self.element
				;
							
			return ~item.toLowerCase().indexOf(element.val().toLowerCase());
		}
		
		, _highlighter: function(item) {
			var self = this
				, element = self.element
				;
				
			var query = element.val().replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
			return item.replace(new RegExp('(' + query + ')', 'ig'), function($1, match) {
				return '<strong>' + match + '</strong>'
			});
		}
		
		, _move: function(e) {
			var self = this;
			
      		if (!self._shown) return;

			switch(e.keyCode) {

				case 38:
					// up arrow
					e.preventDefault();
					self._prev();
					break;

				case 40:
					// down arrow
					e.preventDefault();
					self._next();
					break;
			}

      		e.stopPropagation();			
		}
		
		, _prev: function() {
			var self = this
				, dropdownMenu = self._dropdownMenu
				, active = dropdownMenu.find('.active').removeClass('active')
				, prev = active.prev()
				;

			if(!prev.length) {
				prev = dropdownMenu.find('li').last();
			}

			prev.addClass('active');			
		}
		
		, _next: function() {
			var self = this
				, dropdownMenu = self._dropdownMenu
				, active = dropdownMenu.find('.active').removeClass('active')
				, next = active.next()
				;

			if(!next.length) {
				next = $(dropdownMenu.find('li')[0]);
			}

			next.addClass('active');
		}

		, _click: function (e) {
			var self = this;
			
			e.stopPropagation();
			e.preventDefault();
			self._select();
			self.element.focus();
		}

		, _mouseenter: function (e) {
			var self = this;
			
			self._mousedover = true;
			self._dropdownMenu.find('.active').removeClass('active');
			$(e.currentTarget).addClass('active');
		}

		, _mouseleave: function (e) {
			var self = this;
			
			self._mousedover = false;
			if(!self._focused && self._shown) self._hide();
		}
		
		, _getValueFromDatasourceitem: function(datasourceItem) {
			var self = this;
			
			return self.options.valueField ? datasourceItem[self.options.valueField] : datasourceItem;
		}
		
		, _getLabelFromDatasourceitem: function(datasourceItem) {
			var self = this;
			
			return self.options.labelField ? datasourceItem[self.options.labelField] : datasourceItem;
		}
		
		, _getDSFromOuterDS: function(outerDS) {
			var self = this;
			
			return self.options.ajaxDataField ? outerDS[self.options.ajaxDataField] : outerDS;
		}					
		
		, _setOption: function(key, value) {
			$.Widget.prototype._setOption.apply(this, arguments);
		}
		
		/**
		 * 仅供静态数据源调用
		 */
		, updateDatasource: function(staticDS) {
			var self = this;
			
			if (self.options.ajaxLoad) { // 指定异步加载的不让手动更新数据源
				return;
			} 
			
			self.options.datasource = staticDS;
			self._render();
		}
		
		, destroy: function() {
			$.Widget.prototype.destroy.call(this);
		}
		
	};
	
	$.widget('daniel.typeahead', typeahead);
})(jQuery);
